/**
 * HalfMask - a new approach to password masking security
 *
 * @author    Chris Dary <umbrae@gmail.com>
 * @copyright Copyright (c) 2009 {@link http://arc90.com Arc90 Inc.}
 * @license   http://www.opensource.org/licenses/bsd-license.php
**/

(function($) {
    $.halfmask = {
        settings: {
            halfMaskColors:      ['#300', '#003'],
            numHalfMaskInputs:   2,
            letterSet:           'abcdefghijklmnopqrstuvwxyz',
            fontFamily:          '"Courier New", Courier, Monospace',
            inputAttributes:     ['accessKey','className','disabled','id','maxlength','name','size','style','tabIndex','title','value'], /* Only used for IE */
            halfMaskInputStyles: {
                position:    'absolute',
                background:  'transparent',
                borderColor: 'transparent',
                opacity:     '0.5',
                fontWeight:  'bold'
            }
        }
    };

    $.fn.halfmask = function(settings) {
        /**
         * @var object Contains an associative array of all settings for halfmask.
        **/
        settings = $.extend({}, $.halfmask.settings, settings);

        /**
         * Thankfully provided by Ben Nadel:
         * http://www.bennadel.com/blog/199-Ask-Ben-Creating-Large-Random-Strings-In-Javascript.htm
        **/
        var randRange = function(intFrom, intTo){
            intFrom = Math.floor(intFrom);
            intTo = Math.floor(intTo);

            return Math.floor(intFrom + ((intTo - intFrom + 1) * Math.random()));
        };
        
        /**
         * Mask an input with halfmask. The input must be of type password.
         *
         * Halfmask creates some dummy inputs that will be filled with garbage
         * text with a reduced opacity so that, hopefully, prying eyes can't understand
         * the text just typed, but the original author can.
         *
         * @param selector string A jquery capable selector, as defined here: http://docs.jquery.com/Selectors
         * @return void
        **/
        return this.each(function() {
            var $input, i;
            var $this = $(this);
            
            if(!$this.is('input[type="password"]'))
            {
                throw new Error('HalfMask may only be used on inputs of type password.');
            }

            /** 
             * Replace the password input with one of type text with the same attributes.
             * This allows for graceful degradation, as you cannot change an input's type attribute directly.
            **/
            if($.browser.msie)
            {
                if($.browser.version.substr(0,1) < 7)
                {
                    return false;                   
                }

                $input = $('<input type="text" />');
 
                for(i = 0; i < settings.inputAttributes.length; i++)
                {
                    if(typeof $this.attr(settings.inputAttributes[i]) != 'undefined')
                    {
                        $input.attr(settings.inputAttributes[i], $this.attr(settings.inputAttributes[i]));
                    }
                }
            }
            else
            {
                $input = $this.clone().attr( {
                    type: 'text',
                    spellcheck: false,
                    autocomplete: 'off'
                });             
            }

            $this.before($input).remove();

            /**
             * Save the inputs original dimensions, as changing fonts can
             * alter them if they are determined by the size attribute.
            **/
            var inputDimensions = {width: $input.width(), height: $input.height()};

            $input.css({
                fontFamily: settings.fontFamily,
                width:      inputDimensions.width,
                height:     inputDimensions.height
            });

            var halfMaskInputStyles = $.extend({
                top:         $input.offset().top,
                left:        $input.offset().left,
                fontFamily:  settings.fontFamily
            }, settings.halfMaskInputStyles);

            var halfMaskInputs = [];
            var focusOnInput = function() { $input.focus(); };
            
            for(i = 0; i < settings.numHalfMaskInputs; i++)
            {
                var halfMaskInput = $input.clone().attr({
                    readonly: 'readonly',
                    autocomplete: 'off',
                    tabindex: '99999',
                    id: ($input.attr('id') + '-halfmask-' + i),
                    name: ($input.attr('name') + '-halfmask-' + i)
                });
                halfMaskInput.css($.extend(halfMaskInputStyles, { color: settings.halfMaskColors[i % settings.halfMaskColors.length] }));
                halfMaskInput.click( focusOnInput );

                halfMaskInputs.push(halfMaskInput);
                $input.after(halfMaskInput);
            }
            
            $input.keydown(function(e) {
                /**
                 * Add random characters to our masks to obfuscate the cleartext 'just enough'
                 * so that prying eyes can't understand it.
                **/

                var inputVal = $input.val();
                var keyCode  = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;

                for(var i = 0; i < halfMaskInputs.length; i++)
                {
                    var halfMaskInput = halfMaskInputs[i];

                    if(keyCode < 47 && keyCode != 32 && keyCode !== 0)
                    {
                        return;
                    }
                    
                    halfMaskInput.val(halfMaskInput.val().substr(0, inputVal.length) + settings.letterSet.charAt(randRange(0, settings.letterSet.length-1)));
                }
            }).keyup(function(e) {
                /**
                 * Handle any deletions here by resizing our masks.
                **/

                for(var i = 0; i < halfMaskInputs.length; i++)
                {
                    var halfMaskInput = halfMaskInputs[i];

                    halfMaskInput.val(halfMaskInput.val().substr(0, $input.val().length));
                }
            });

        });
    };
    
})(jQuery);
